package gae.testjig.task;

import gae.testjig.client.ClientChannelId;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TaskOptions.Method;

public class Task {
	
	final static public Task BUILD = new Task();
	final static public Task DELAY2 = new Task(2000); // minimum two second delay
	
	final static private String BASE = "/tasks";
	
	final static String PATH_ADVANCE_STATE		= "/advance";
	final static String PATH_COUNT_INVOCATION	= "/count";

	final static String PATH_ADVANCE_STATE_DS		= "/ds_advance";
	final static String PATH_COUNT_INVOCATION_DS	= "/ds_count";

	final static String PATH_CONSTRAINT_TEST		= "/constraint_test";
	final static String PATH_CONSTRAINT_TEST_RACE	= "/constraint_test_race";
	final static String PATH_CONSTRAINT_TEST_RACE_OFY	= "/constraint_test_race_ofy";

	final static public Queue TX_TEST_QUEUE = QueueFactory.getQueue("tx-test-queue");
	final static public Queue COUNTER_QUEUE = QueueFactory.getQueue("counter-queue");
	

	private long minDelay = 0;
	
	public Task() {
		this(0);
	}

	public Task(long minDelay) {
		this.minDelay = minDelay;
	}
	
	private void applyExactDelay(TaskOptions taskOptions, long delay) {
		long useDelay = Math.max(0, delay);
		taskOptions.countdownMillis(useDelay);
	}

	private void applyDelay(TaskOptions taskOptions, long delay) {
		long useDelay = Math.max(this.minDelay, delay);
		taskOptions.countdownMillis(useDelay);
	}

	public TaskOptions advanceStateDS(ClientChannelId ccid, String stateMachineKeyString, String counterKeyString, int nextState, int isDelay) {
		return advanceState_(BASE + PATH_ADVANCE_STATE_DS, ccid, stateMachineKeyString, counterKeyString, nextState, isDelay);
	}

	public TaskOptions advanceState(ClientChannelId ccid, String stateMachineKeyString, String counterKeyString, int nextState, int isDelay) {
		return advanceState_(BASE + PATH_ADVANCE_STATE, ccid, stateMachineKeyString, counterKeyString, nextState, isDelay);
	}

	private TaskOptions advanceState_(String base, ClientChannelId ccid, String stateMachineKeyString, String counterKeyString, int nextState, int isDelay) {
		try {
			TaskOptions taskOptions = TaskOptions.Builder.withUrl(
					base +
					"?clientId=" + URLEncoder.encode(ccid.getClientId(), "UTF-8") +
					"&channelId=" + URLEncoder.encode(ccid.getChannelId(), "UTF-8") +
					"&stateMachineKey=" + URLEncoder.encode(stateMachineKeyString, "UTF-8") +
					"&counterKey=" + URLEncoder.encode(counterKeyString, "UTF-8") +
					"&nextState=" + nextState +
					"&isDelay=" + isDelay
			).method(Method.GET);
			applyDelay(taskOptions, 0);
			return taskOptions;
		}
		catch (UnsupportedEncodingException e) {
			// this happens only if I didn't type "UTF-8" correctly above.
			throw new RuntimeException("url encoding failed. " + e.getMessage());
		}
	}

	public TaskOptions countInvocationDS(ClientChannelId ccid, String counterKeyString, int state, int invocationCount, int retryCount, int mismatches) {
		return countInvocation(BASE + PATH_COUNT_INVOCATION_DS, ccid, counterKeyString, state, invocationCount, retryCount, mismatches);
	}
	public TaskOptions countInvocation(ClientChannelId ccid, String counterKeyString, int state, int invocationCount, int retryCount, int mismatches) {
		return countInvocation(BASE + PATH_COUNT_INVOCATION, ccid, counterKeyString, state, invocationCount, retryCount, mismatches);
	}
	private TaskOptions countInvocation(String base, ClientChannelId ccid, String counterKeyString, int state, int invocationCount, int retryCount, int mismatches) {
		try {
			TaskOptions taskOptions = TaskOptions.Builder.withUrl(
				base +
					"?clientId=" + URLEncoder.encode(ccid.getClientId(), "UTF-8") +
					"&channelId=" + URLEncoder.encode(ccid.getChannelId(), "UTF-8") +
					"&counterKey=" + URLEncoder.encode(counterKeyString, "UTF-8") +
					"&state=" + state +
					"&invocationCount=" + invocationCount +
					"&retryCount=" + retryCount +
					"&mismatches=" + mismatches
			).method(Method.GET);
			applyDelay(taskOptions, 0);
			return taskOptions;
		}
		catch (UnsupportedEncodingException e) {
			// this happens only if I didn't type "UTF-8" correctly above.
			throw new RuntimeException("url encoding failed. " + e.getMessage());
		}
	}
	
	public TaskOptions constraintTest(ClientChannelId ccid, String ownerName, String seatId) {
		try {
			TaskOptions taskOptions = TaskOptions.Builder.withUrl(
				BASE + PATH_CONSTRAINT_TEST +
					"?clientId=" + URLEncoder.encode(ccid.getClientId(), "UTF-8") +
					"&channelId=" + URLEncoder.encode(ccid.getChannelId(), "UTF-8") +
					"&ownerName=" + URLEncoder.encode(ownerName, "UTF-8") +
					"&seatId=" + URLEncoder.encode(seatId, "UTF-8")
			).method(Method.GET);
			applyDelay(taskOptions, 0);
			return taskOptions;
		}
		catch (UnsupportedEncodingException e) {
			// this happens only if I didn't type "UTF-8" correctly above.
			throw new RuntimeException("url encoding failed. " + e.getMessage());
		}
	}

	public TaskOptions constraintTestRace(ClientChannelId ccid, String ownerName, String seatId, long runAtTimestamp) {
		try {
			TaskOptions taskOptions = TaskOptions.Builder.withUrl(
				BASE + PATH_CONSTRAINT_TEST_RACE +
					"?clientId=" + URLEncoder.encode(ccid.getClientId(), "UTF-8") +
					"&channelId=" + URLEncoder.encode(ccid.getChannelId(), "UTF-8") +
					"&ownerName=" + URLEncoder.encode(ownerName, "UTF-8") +
					"&seatId=" + URLEncoder.encode(seatId, "UTF-8")
			).method(Method.GET);
//			applyDelay(taskOptions, 0);
			long now = System.currentTimeMillis();
			applyExactDelay(taskOptions, runAtTimestamp - now);
			return taskOptions;
		}
		catch (UnsupportedEncodingException e) {
			// this happens only if I didn't type "UTF-8" correctly above.
			throw new RuntimeException("url encoding failed. " + e.getMessage());
		}
	}
	
	public TaskOptions constraintTestRaceOfy(ClientChannelId ccid, String ownerName, String seatId, long runAtTimestamp) {
		try {
			TaskOptions taskOptions = TaskOptions.Builder.withUrl(
				BASE + PATH_CONSTRAINT_TEST_RACE_OFY +
					"?clientId=" + URLEncoder.encode(ccid.getClientId(), "UTF-8") +
					"&channelId=" + URLEncoder.encode(ccid.getChannelId(), "UTF-8") +
					"&ownerName=" + URLEncoder.encode(ownerName, "UTF-8") +
					"&seatId=" + URLEncoder.encode(seatId, "UTF-8")
			).method(Method.GET);
//			applyDelay(taskOptions, 0);
			long now = System.currentTimeMillis();
			applyExactDelay(taskOptions, runAtTimestamp - now);
			return taskOptions;
		}
		catch (UnsupportedEncodingException e) {
			// this happens only if I didn't type "UTF-8" correctly above.
			throw new RuntimeException("url encoding failed. " + e.getMessage());
		}
	}

}